package com.xuecheng.content.service.impl;

import com.alibaba.druid.sql.visitor.functions.If;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.api.content.model.dto.CourseBaseDTO;

import com.xuecheng.api.content.model.dto.TeachplanDTO;
import com.xuecheng.api.content.model.qo.QueryCourseModel;
import com.xuecheng.api.content.system.model.dto.CourseCategoryDTO;
import com.xuecheng.common.domain.code.CommonErrorCode;
import com.xuecheng.common.domain.page.PageRequestParams;
import com.xuecheng.common.domain.page.PageVO;
import com.xuecheng.common.domain.response.RestResponse;
import com.xuecheng.common.enums.common.CommonEnum;
import com.xuecheng.common.enums.content.CourseAuditEnum;
import com.xuecheng.common.enums.content.CourseChargeEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.common.util.JsonUtil;
import com.xuecheng.common.util.StringUtil;
import com.xuecheng.content.agent.SystemApiAgent;
import com.xuecheng.content.common.constant.ContentErrorCode;
import com.xuecheng.content.controller.CourseAuditController;
import com.xuecheng.content.convert.CourseBaseConvert;
import com.xuecheng.content.convert.CoursePubConvert;
import com.xuecheng.content.entity.CourseBase;
import com.xuecheng.content.entity.CourseMarket;
import com.xuecheng.content.entity.CoursePub;
import com.xuecheng.content.entity.CourseTeacher;
import com.xuecheng.content.mapper.CourseBaseMapper;
import com.xuecheng.content.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 课程基本信息 服务实现类
 * </p>
 *
 * @author itcast
 */
@Slf4j
@Service
public class CourseBaseServiceImpl extends ServiceImpl<CourseBaseMapper, CourseBase> implements CourseBaseService {
    @Autowired
    private CourseMarketService courseMarketService;
    @Autowired
    private CourseBaseService courseBaseService;
    @Autowired
    private TeachplanService teachplanService;

    @Autowired
    private CourseTeacherService courseTeacherService;
    @Autowired
    private CoursePubService coursePubService;

    /*
     * 实现步骤：
     *   1.是否需要开启事务（查询不需要开启，增删改时需要开启事务）
     *   2.判断关键数据（代码健壮性）
     *       关键数据：数据来源是前端
     *           分页数据需要判断：pageNo、pageSize
     *           查询添加对象数据不需要判断
     *   3.构建Page（MP）对象
     *   4.构建查询构建对象
     *   5.查询数据
     *       分页对象Page
     *       查询构建对象
     *   6.将查询结果封装并返回
     * */
    public PageVO queryCourseBaselist(PageRequestParams params, QueryCourseModel model, Long companyId) {
        // 2.判断关键数据（代码健壮性）
        //      关键数据：数据来源是前端
        //          分页数据需要判断：pageNo、pageSize
        if (params.getPageNo() < 1) {
            params.setPageNo(PageRequestParams.DEFAULT_PAGE_NUM);
        }

        if (params.getPageSize() < 1) {
            params.setPageSize(PageRequestParams.DEFAULT_PAGE_SIZE);
        }
        //  3.构建Page（MP）对象
        Page<CourseBase> page = new Page<>(params.getPageNo(), params.getPageSize());
        //  4.构建查询构建对象
        LambdaQueryWrapper<CourseBase> queryWrapper = new LambdaQueryWrapper<>();
        // select * from course_base where name like ? and audit_status = ?
        // 完整版
        // // 课程名称(模糊匹配)
        // if (StringUtil.isNotBlank(model.getCourseName())) {
        //     queryWrapper.like(CourseBase::getName, model.getCourseName());
        // }
        // // 课程审核状态（精确匹配）
        // if (StringUtil.isNotBlank(model.getAuditStatus())) {
        //     queryWrapper.eq(CourseBase::getAuditStatus, model.getAuditStatus());
        // }
        // 简化版
        queryWrapper.like(StringUtil.isNotBlank(model.getCourseName()), CourseBase::getName, model.getCourseName());
        queryWrapper.eq(StringUtil.isNotBlank(model.getAuditStatus()), CourseBase::getAuditStatus, model.getAuditStatus());

        if (!(ObjectUtils.nullSafeEquals(companyId,
                CourseAuditController.OPERATION_FLAG))) {
            queryWrapper.eq(CourseBase::getCompanyId, companyId);
        }
        queryWrapper.eq(CourseBase::getStatus, CommonEnum.USING_FLAG.getCodeInt());

        //  5.查询数据
        //      分页对象Page
        //      查询构建对象
        Page<CourseBase> pageResult = this.page(page, queryWrapper);

        List<CourseBase> records = pageResult.getRecords();
        long total = pageResult.getTotal();


        List<CourseBaseDTO> dtos = Collections.EMPTY_LIST;
        if (!(CollectionUtils.isEmpty(records))) {
            dtos = new ArrayList<>();
            dtos = CourseBaseConvert.INSTANCE.entitys2dtos(records);
        }

        //  6.将查询结果封装并返回-->PageVO
        PageVO pageVO = new PageVO<>(dtos, total, params.getPageNo(), params.getPageSize());


        return pageVO;
    }

    @Override
    @Transactional
    public CourseBaseDTO createCourseBase(CourseBaseDTO courseBaseDTO) {

//         * 业务分析：
//         *   1.是否要开启事务（查询不需要开启，增删改时需要开启事务）
//         *   2.判断关键数据
//         *       关键数据：数据来源是前端
//         *       添加时的必要数据：
//         *           1.从数据库结构-非空字段
//         *           2.前端的必要数据（红色*）
//         *           3.接口文档Yapi
//         *       数据内容：companyId、name、mt、st、grade、teachmode、users、pic、charge、price（收费课程需要判断）
//         *       如果必要数据没有给，告诉前端数据必填：抛出异常（终止程序、传递错误信息）
        if (ObjectUtils.isEmpty(courseBaseDTO.getCompanyId())) {
            throw new RuntimeException("公司id不能为空");
        }
        if (StringUtil.isBlank(courseBaseDTO.getName())) {
            throw new RuntimeException("课程名称不能为空");
        }

        if (StringUtil.isBlank(courseBaseDTO.getMt())) {
            throw new RuntimeException("课程大分类不能为空");
        }

        if (StringUtil.isBlank(courseBaseDTO.getSt())) {
            throw new RuntimeException("课程小分类不能为空");
        }

        if (StringUtil.isBlank(courseBaseDTO.getGrade())) {
            throw new RuntimeException("课程等级不能为空");
        }

        if (StringUtil.isBlank(courseBaseDTO.getTeachmode())) {
            throw new RuntimeException("课程教学模式不能为空");
        }

        if (StringUtil.isBlank(courseBaseDTO.getUsers())) {
            throw new RuntimeException("使用人群不能为空");
        }

        if (StringUtil.isBlank(courseBaseDTO.getCharge())) {
            throw new RuntimeException("课程收费不能为空");
        }
        // 判断收费课程，价格不能为空，必须要大于0
        if (CourseChargeEnum.CHARGE_YES.getCode().equals(courseBaseDTO.getCharge())) {
            if (ObjectUtils.isEmpty(courseBaseDTO.getPic()) || courseBaseDTO.getPrice().floatValue() < 0) {
                throw new RuntimeException("课程价格不合法，亲 请输入合法的价格");
            }
        } else {
            //课程免费情况 价格为0
            courseBaseDTO.setPrice(new BigDecimal("0"));
        }

//         *   3.将dto数据转为po数据
//         *       po数据都是全都传过来的信息
//         *       po中对于新增数据一些字段需要在业务层进行赋值
//         *           auditStatus
//         *           status（数据库会默认赋值）
//         *           createdate（mp自动填充）
//         *           changedate（mp自动填充）
        CourseBase courseBase = CourseBaseConvert.INSTANCE.dto2entity(courseBaseDTO);
        //auditStatus;新增课程状态为--》未提交
        courseBase.setAuditStatus(CourseAuditEnum.AUDIT_UNPAST_STATUS.getCode());
//         *   4.保持数据-判断保持的结果
//         *       PS：要先保持课程基础信息表，课程营销表要记录coursebaseid
//         *       课程基础信息表
//         *       课程营销表
//         *       PS：如果保持两张表时，其中有一张表保存失败，需要让全部的数据进行回滚：抛出异常
        boolean baseRusult = this.save(courseBase);
        if (!baseRusult) {
            throw new RuntimeException("课程基本数据信息保存失败");
        }
        //课程营销表
        CourseMarket courseMarket = new CourseMarket();
        courseMarket.setCourseId(courseBase.getId());
        courseMarket.setCharge(courseBaseDTO.getCharge());
        courseMarket.setPrice(courseBaseDTO.getPrice().floatValue());

        boolean marketResult = courseMarketService.save(courseMarket);

        if (!marketResult) {
            throw new RuntimeException("课程营销保存失败");
        }
//         *   5.返回数据库最新的数据DTO
        CourseBase po = this.getById(courseBase.getId());

        CourseBaseDTO resultDTO = CourseBaseConvert.INSTANCE.entity2dto(po);

        resultDTO.setCharge(courseBaseDTO.getCharge());
        resultDTO.setPrice(courseBaseDTO.getPrice());

        return resultDTO;

    }

    @Override
    @Transactional
    public CourseBaseDTO getCourseBaseById(Long courseBaseId, Long companyId) {
        /*
     * 业务分析：
     *   1.是否要开启事务
     *   	增删改需要开启事务
     *   	方式：在方法上添加@Transactional
     *   2.判断关键数据
     *   	前端传递过来的数据
     *   	需要比新增时要多判断一个数据：courseBaseId
     *   		修改课程基础信息是必须携带id值的
     *   3.判断业务数据
     *   	判断是否存在
     *   	判断是否是同一家机构
     *   	判断是否删除
     *   	判断审核状态
     *   		未提交--新创建出的课程
     *   		审核未通过--运营审核没有通过
     *   		只有这两个状态才可以修改课程基础信息
     *   4.将dto转为po
     *   5.保存数据
            CourseBase
            CourseMarket
         6.将修改后的最新数据返回给前端
     * */
        //判断关键数据   前端传递的数据为关键数据
        // courseBaseId
        //companyId
        if (ObjectUtils.isEmpty(courseBaseId) || ObjectUtils.isEmpty(companyId)) {
            throw new RuntimeException("传入参数与接口不匹配");
        }
        //判断业务数据
        //1 判断课程是否存在 2 判断是否同一家教育机构 3 判断是否删除
        CourseBase courseBase = getCourseBaseByBaseId(courseBaseId, companyId);
        //查询数据并装为dto返回
        CourseBaseDTO courseBaseDTO = CourseBaseConvert.INSTANCE.entity2dto(courseBase);
        //课程营销
        CourseMarket courseMarket = getCourseMarketByCourseId(courseBaseId);
        courseBaseDTO.setCharge(courseMarket.getCharge());
        courseBaseDTO.setPrice(new BigDecimal(courseMarket.getPrice().toString()));
//返回结果
        return courseBaseDTO;
    }

    @Override
    @Transactional
    public CourseBaseDTO modifyCourseBase(CourseBaseDTO dto) {
         /*
     * 业务分析：
     *   1.是否要开启事务
     *   	增删改需要开启事务   *******
     *   	方式：在方法上添加@Transactional
     *   2.判断关键数据
     *   	前端传递过来的数据
     *   	需要比新增时要多判断一个数据：courseBaseId
     *   		修改课程基础信息是必须携带id值的
     *
     *   3.判断业务数据
     *   	判断是否存在
     *   	判断是否是同一家机构
     *   	判断是否删除
     *   	判断审核状态
     *   		未提交--新创建出的课程
     *   		审核未通过--运营审核没有通过
     *   		只有这两个状态才可以修改课程基础信息
     *   4.将dto转为po
     *   5.保存数据
            CourseBase
            CourseMarket
         6.将修改后的最新数据返回给前端
     * */
        /**2.判断关键数据
         前端传递过来的数据
         需要比新增时要多判断一个数据：courseBaseId
         修改课程基础信息是必须携带id值的
         */
        verifyCourseMsg(dto);
        Long courseBaseId = dto.getCourseBaseId();
        if (ObjectUtils.isEmpty(courseBaseId)) {
            throw new RuntimeException("课程id不能为空");
        }
        /**3.判断业务数据
         判断是否存在
         判断是否是同一家机构
         判断是否删除
         判断审核状态
         未提交--新创建出的课程
         审核未通过--运营审核没有通过
         只有这两个状态才可以修改课程基础信息
         *
         */
        getCourseByLogic(dto.getCompanyId(), courseBaseId);
        //课程营销
        CourseMarket courseMarket = getCourseMarketByCourseId(courseBaseId);
        //将dto转为po
        //课程基础信息表
        // companyid auditstatus status
        CourseBase po = CourseBaseConvert.INSTANCE.dto2entity(dto);
        //保存数据
        //为了防止前端恶意修改数据内容
        po.setCompanyId(null);
        po.setAuditStatus(null);
        po.setStatus(null);
        boolean baseResult = this.updateById(po);
        if (!baseResult) {
            throw new RuntimeException("课程修改失败了qaq");
        }
        //此代码不理解 需要后期提问思考
        //     课程营销数据表
        //         charge price
        // 如果使用mq修改一张表中的极个别数据，UpdateWrapper
        // update course_market set charge=xxx,price=xxx  where courseid=xx
        LambdaUpdateWrapper<CourseMarket> marketUpdateWrapper = new LambdaUpdateWrapper<>();
        marketUpdateWrapper.set(CourseMarket::getCharge, dto.getCharge());

        String charge = dto.getCharge();
        if (CourseChargeEnum.CHARGE_YES.getCode().equals(charge)) {

            BigDecimal price = dto.getPrice();
            if (ObjectUtils.isEmpty(price)) {
                throw new RuntimeException("收费课程价格不能为空");
            }

            marketUpdateWrapper.set(CourseMarket::getPrice, dto.getPrice().floatValue());
        } else {
            // 如果课程为免费，需要价格赋值为0
            marketUpdateWrapper.set(CourseMarket::getPrice, 0F);
        }
        marketUpdateWrapper.eq(CourseMarket::getCourseId, courseBaseId);


        boolean marketResult = courseMarketService.update(marketUpdateWrapper);

        //      修改数据时要判断修改后结果
        if (!marketResult) {
            throw new RuntimeException("修改课程营销数据失败");
        }


        // 6.将修改后的最新数据返回给前端
        CourseBaseDTO resultDTO = getLastCourseBaseDTO(dto, courseBaseId);

        return resultDTO;
    }

    @Override
    @Transactional
    public void removeCourseBase(Long courseBaseId, Long companyId) {/*
    * 业务分析：
        1.是否要开启事务
            需要开启
        2.判断关键数据
            courseBaseId
            companyid
        3.判断业务数据
            判断课程是否存在
            判断是否是同一家机构
            判断课程是否已经删除
            判断课程的审核状态
                未提交
                审核未通过
                只有这些状态才可以被删除
        4.删除课程基础信息（根据id删除）
            修改数据库的数据status
    * */
        if (ObjectUtils.isEmpty(courseBaseId) || ObjectUtils.isEmpty(companyId)) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }
        getCourseByLogic(companyId, courseBaseId);
        //由于数据库的数据不会轻易删除所以我们先做判断 然后决定是否真的需要删除
        //修改数据库的数据status
        LambdaUpdateWrapper<CourseBase> objectLambdaUpdateWrapper = new LambdaUpdateWrapper<>();

        objectLambdaUpdateWrapper.set(CourseBase::getChangeDate, LocalDateTime.now());

        objectLambdaUpdateWrapper.eq(CourseBase::getId, courseBaseId);

        boolean result = update(objectLambdaUpdateWrapper);
        if (!result) {
            ExceptionCast.cast(ContentErrorCode.E_120017);
        }

    }

    @Override
    @Transactional
    public void commitCourseBase(Long courseBaseId, Long companyId) {
        /*
         * 业务分析：
         *   1.判断关键数据
         *   2.判断业务数据
         *       课程基础信息
         *           判断是否存在
         *           判断是否是同一家教学机构
         *           判断是否删除
         *           判断审核状态
         *   3.修改课程审核状态
         *       已提交
         * */
        if (ObjectUtils.isEmpty(courseBaseId) ||
                ObjectUtils.isEmpty(companyId)
        ) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }
        getCourseByLogic(companyId, courseBaseId);
//        修改课程审核状态
//        //     已提交
//        //  createDate  changeDate 使用MP自动填充来完成
//        //  UpdateWrapper 会将MP的自动填充功能失效
        LambdaUpdateWrapper<CourseBase> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(CourseBase::getAuditStatus, CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode());
        updateWrapper.set(CourseBase::getChangeDate, LocalDateTime.now());
        updateWrapper.eq(CourseBase::getId, courseBaseId);
        boolean result = this.update(updateWrapper);
        if (!result) {
            ExceptionCast.cast(ContentErrorCode.E_120017);
        }


    }

    @Override
    public void approve(CourseBaseDTO dto) {
        //其他代码省略
        /*
         * 业务分析：
         *   1.判断关键数据
         *       auditMind  auditStatus  courseid
         *
         *   2.判断业务数据
         *       课程基础信息
         *           判断是否存在
         *           判断是否删除
         *           判断审核状态
         *               必须为：已提交
         *       审核状态
         *           运营平台只能给课程审核状态赋值：审核通过、审核未通过
         *   3.修改课程审核信息
         *       auditMind  auditStatus  auditNum（每次+1）
         * */
        // 1.判断关键数据
        //      auditMind  auditStatus  courseid
        if (StringUtil.isBlank(dto.getAuditMind()) ||
                StringUtil.isBlank(dto.getAuditStatus()) ||
                ObjectUtils.isEmpty(dto.getCourseBaseId())
        ) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }
        //  2.判断业务数据
        //      课程基础信息
        //          判断是否存在
        //          判断是否删除
        //          判断审核状态
        //              必须为：已提交
        LambdaQueryWrapper<CourseBase> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(CourseBase::getId, dto.getCourseBaseId());
        queryWrapper.eq(CourseBase::getStatus, CommonEnum.USING_FLAG.getCodeInt());
        queryWrapper.eq(CourseBase::getAuditStatus, CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode());
        int count = this.count(queryWrapper);
        if (count < 1) {
            ExceptionCast.cast(ContentErrorCode.E_120023);
        }
        //      审核状态
        //          运营平台只能给课程审核状态赋值：审核通过、审核未通过
        String auditStatus = dto.getAuditStatus();
        if (CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(auditStatus) ||
                CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(auditStatus) ||
                CourseAuditEnum.AUDIT_UNPAST_STATUS.getCode().equals(auditStatus)
        ) {
            ExceptionCast.cast(ContentErrorCode.E_120016);
        }
        //  3.修改课程审核信息
        //      auditMind  auditStatus  auditNum（每次+1）
        LambdaUpdateWrapper<CourseBase> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(CourseBase::getAuditStatus, dto.getAuditStatus());
        updateWrapper.set(CourseBase::getAuditMind, dto.getAuditMind());
        // 使用redis来实现
        // updateWrapper.set(CourseBase::getAuditNums, dto.getAuditMind());
        updateWrapper.set(CourseBase::getChangeDate, LocalDateTime.now());
        updateWrapper.eq(CourseBase::getId, dto.getCourseBaseId());
        boolean result = this.update(updateWrapper);
        if (!result) {
            ExceptionCast.cast(ContentErrorCode.E_120017);
        }

    }




    //判断课程状态方法
    private CourseBase getCourseBaseByBaseId(Long courseBaseId, Long companyId) {
        // select * from course_base where id = ? and companyId = ?
        LambdaQueryWrapper<CourseBase> baseQueryWrapper = new LambdaQueryWrapper<>();
        baseQueryWrapper.eq(CourseBase::getId, courseBaseId);
        baseQueryWrapper.eq(CourseBase::getCompanyId, companyId);
        // baseQueryWrapper.eq(CourseBase::getStatus, CommonEnum.USING_FLAG.getCodeInt());

        CourseBase courseBase = this.getOne(baseQueryWrapper);

        if (ObjectUtils.isEmpty(courseBase)) {
            throw new RuntimeException("课程不存在");
        }

        Integer status = courseBase.getStatus();
        if (!(CommonEnum.USING_FLAG.getCodeInt().equals(status))) {
            throw new RuntimeException("课程信息已经被删除");
        }
        return courseBase;
    }

    private CourseMarket getCourseMarketByCourseId(Long courseBaseId) {
        LambdaQueryWrapper<CourseMarket> marketQueryWrapper = new LambdaQueryWrapper<>();
        marketQueryWrapper.eq(CourseMarket::getCourseId, courseBaseId);

        CourseMarket courseMarket = courseMarketService.getOne(marketQueryWrapper);

        if (ObjectUtils.isEmpty(courseMarket)) {
            throw new RuntimeException("课程营销数据不存在");
        }
        return courseMarket;
    }

    private void verifyCourseMsg(CourseBaseDTO dto) {
        if (ObjectUtils.isEmpty(dto.getCompanyId())) {
            // 业务异常：程序员在做业务判断时，数据有问题。
            // 异常：
            //    1.终止程序
            //    2.传递错误信息
            //    3.使用运行时异常来抛出
            //        运行时异常不需在编译期间处理
            ExceptionCast.cast(ContentErrorCode.E_120018);
        }

        if (StringUtil.isBlank(dto.getName())) {
            ExceptionCast.cast(ContentErrorCode.E_120004);
        }

        if (StringUtil.isBlank(dto.getMt())) {
            ExceptionCast.cast(ContentErrorCode.E_120002);
        }

        if (StringUtil.isBlank(dto.getSt())) {
            ExceptionCast.cast(ContentErrorCode.E_120003);
        }

        if (StringUtil.isBlank(dto.getGrade())) {
            ExceptionCast.cast(ContentErrorCode.E_120007);
        }

        if (StringUtil.isBlank(dto.getTeachmode())) {
            ExceptionCast.cast(ContentErrorCode.E_120006);
        }

        if (StringUtil.isBlank(dto.getUsers())) {
            ExceptionCast.cast(ContentErrorCode.E_120019);
        }

        if (StringUtil.isBlank(dto.getCharge())) {
            ExceptionCast.cast(ContentErrorCode.E_120020);
        }
    }

    private void getCourseByLogic(Long companyId, Long courseBaseId) {

        CourseBase courseBase = getCourseBaseByBaseId(courseBaseId, companyId);

        String auditStatus = courseBase.getAuditStatus();
        if (CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus) ||
                CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(auditStatus) ||
                CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(auditStatus)
        ) {
            throw new RuntimeException("课程审核状态异常");
        }
    }

    private CourseBaseDTO getLastCourseBaseDTO(CourseBaseDTO dto, Long id) {
        CourseBase po = this.getById(id);

        CourseBaseDTO resultDTO = CourseBaseConvert.INSTANCE.entity2dto(po);
        resultDTO.setCharge(dto.getCharge());
        resultDTO.setPrice(dto.getPrice());
        return resultDTO;
    }

    /**
     * 1.判断关键数据
     * companyId
     * courseId
     */



}
